package co.baiku.boot.common.tools;

import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;

/**
 * 异步重试工具
 */
public class AsyncTask {

    public static final AsyncTask ME = new AsyncTask();

    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    private ExecutorService executor;

    private AsyncTask() {

    }

    /**
     * 自定义异步重试
     *
     * @param t       输入参数
     * @param handler 处理逻辑
     * @param <T>     输入
     * @param <R>     输出
     * @return
     */
    public <T, R> R execute(T t, Function<T, R> handler) {
        int totalCount = 3;
        return execute(t, totalCount, handler);
    }

    /**
     * 自定义异步重试
     *
     * @param t               输入参数
     * @param totalRetryCount 重试次数
     * @param handler         处理逻辑
     * @param <T>             输入
     * @param <R>             输出
     * @return
     */
    public <T, R> R execute(T t, int totalRetryCount, Function<T, R> handler) {
        if (executor == null) {
            executor = new ScheduledThreadPoolExecutor(20, new BasicThreadFactory.Builder().namingPattern("async-pool-%d").daemon(true).build());
        }
        CountDownLatch downLatch = new CountDownLatch(1);
        final AtomicInteger retryCount = new AtomicInteger(totalRetryCount);
        final Map<String, R> data = new HashMap<>();
        executor.execute(() -> {
            try {
                while (retryCount.get() > 0) {
                    try {
                        data.put("result", handler.apply(t));
                        break;
                    } catch (Exception e) {
                        e.printStackTrace();
                        retryCount.getAndAdd(-1);
                    }
                    logger.debug("重试第" + (totalRetryCount - retryCount.get()) + "次");
                }
            } finally {
                downLatch.countDown();
            }
        });
        try {
            downLatch.await();
            return data.get("result");
        } catch (InterruptedException e) {
            return null;
        }
    }

}
